Lua
Lua Scripting language for CHDK Additionally to the "UBASIC" scripting language newer CHDK versions (Juciphox branch or CHDK versions since v0.5/changeset #523) provide a new and more powerful alternative scripting language named "Lua". So what is Lua ? Lua is a powerful, fast, light-weight, embeddable scripting language. Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode for a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping. (quoted from www.lua.org) Lua was brought to CHDK by "Velo" in the forum in April 2008: Lua Scripting Integration License: Lua is Open Source software, its licenses are compatible with GPL. Important Links Lua dependend CHDK wikia articles: *Lua Syntax *Lua Command Reference External links: *'The programming language Lua - Homepage' :→NEWS :→Reference Manuals :→License Running LUA scripts *A CHDK version with built-in LUA support is needed, e.g. CHDK builds > version 0.5 / changeset 512 *The script should be saved with the filename extension .lua in the CHDK script folder *LUA scripts support libraries, these files has to be placed either in or , see LUA syntax: Libraries. *Loading, starting, pausing & stopping works exactly in the same way as on UBASIC scripts *UBASIC scipts are also working as usual, the filename extension (.lua or .bas) tells CHDK how to run the script *For advanced Lua development, you may with to run scripts on your computer. Set up a LUA Dev Environment A first LUA sample script: for i = 1,10 do print( "Picture "..i ) shoot() sleep( 2000 ) end LUA standard libraries: io, os, string, and math CHDK has included experimental support for the lua standard IO and OS libraries since changeset 517. These allow you to manipulate files and directories. See this post for important differences, limitations and warnings. Support for the string library has been added in changeset 606. Support for the math library exists in a modified form. Lua numbers are floats but chdk lua uses 32bit ints instead. The portions of the math library that make sense for ints are included. →''' A '''test script to check the correct functionality of the library functions (including a list of successfully tested cameras) is also available: [[LUA#lua IO & OS library test|'LUA IO+OS library test']] ! Differences from standard lua iolib Missing features * io.popen: CHDK has no other processes, no pipes * standard file handles: stdin/stdout/stderr all start out closed, as do io.input() and io.output() * io.tmpfile: Not implemented yet, but may be added later. Added features * file:_getfptr() ::fptrval=file:_getfptr() :Returns the actual FILE * associated with the file object. This is strictly for development/debugging, but combined with peek makes verifying FILE structure hack much simpler. Other differences * Error detection and reporting is more limited: :: vxworks reports errno, but no text description :: dryos has no errno (or not found until now), and simply reports "error" * file:read("*n") will behave slightly differently because it doesn't use scanf. Shouldn't matter for sanely structured files, but corner cases will be different. Differences from standard lua oslib Missing features * os.clock: not implemented. Note this relates to CPU time used by a process, not time of day (which is handled by os.time and os.date) * os.execute: no processes, revisit for ELF edition * os.exit: not meaningful for CHDK * os.getenv: environment variables exist in vxworks, but usefulness is doubtful * os.setlocale: functions may exist, but it's unlikely the rest of the firmware uses them * os.tmpname: TODO, not implemented yet (a beta implementation is available in the forum). Also trivial to implement entirely in lua script. Added features The following are not in the standard lua OS lib, but are added in CHDK * os.mkdir ::status[,errstring,errno]=os.mkdir("A/path") :Attempts to create a directory at the named path. Returns true on success or nil,"error string",errno. * os.stat (implemented since build 0.6.5 / changeset 541) ::stat_table[,errstring,errno=os.stat("filename") :Returns a table on success, or nil followed by the values of strerror and errno :The table has the following fields (all numbers) :: :The following boolean values are set based on attrib, so you don't have to do bit testing for the most common values of interest: ::is_dir ::is_file :Notes: :* It is possible to stat things that are neither file nor directory, e.g. volume labels. So you should test for exactly what you want, rather than assuming that not is_file means directory. :* Files larger than 2gb would appear to have a negative size in lua. * os.utime (implemented since build 0.6.5 / changeset 541) ::status[,errstring,errno]=os.utime("filename"[,atime,mtime]) :Set the modification and access time of the named file. If either time is missing or nil, the current time is used. Returns true on success, or nil, "error message", errno. To set one time and leave the other unchanged, you must use stat to get the current value. * os.listdir (implemented since build 0.7.4 / changeset 555) ::dir_table[,errstring,errno]=os.listdir("dirpath",showall) :List the contents of a directory. Returns array of file names, or nil, "error message", errno. :If showall is true, the array includes ".", ".." and deleted entries :Sample/Usage: see LUA#Listdir_sample :Notes: :* Except for the root directory, names ending in / will not work. (untested on DryOS) :* DryOS only returns 8.3 names. VxWorks returns long names. :* DryOS will report success and return an empty table if you call listdir on a file. Other differences * Similar error reporting limitations to io * os.date: Does not distinguish between local and GMT. It's possible support could be added for some cameras. * os.difftime: Just returns (unsigned int)t1 - (unsigned int)t2 General notes from IO and OS testing on vxworks * os.remove works on files and empty directories. Attempting to remove a non-empty directory fails gracefully. * os.rename works on files and empty directories. Attempting to rename a non-empty directory may result in filesystem corruption ! * os.rename only works within a given directory. You can not use rename to move a file to another directory. Attempting to do so may cause unpredictable results. * os.rename may produce unpredictable results if the target name already exists. * os.mkdir will not create a directory if the name has a trailing /, but fails gracefully * io functions are very likely limited to files of less that two gigabytes, and certainly less than four. * unlike some OSes, file:seek cannot be used to extend a file Future versions should attempt to handle some of these situations more gracefully. Dryos may behave differently. The string library Unlike io and os, the string library does not differ substantially from the lua standard. If it fails on your camera, the worst result is likely to be a crash. :→''' Lua Reference Manual - String Manipulation :→''' Programming in Lua - The String Library LUA library test If the libraries have not already been tested on your camera and firmware version, you should run the script before attempting to use them. This script tests the various IO and OS functions, generating /llibtst.log and reporting pass or fail in the console. If not all tests are run, or there are failures, it may leave other files or directories in the root. The following cameras have been successfully tested as of March 15, 2009: a550-100c a560-100a a570-1.00e a570-101a a590-101b a620-100f a630-100c a650-1.00d a710-100a a720-100c ixus40_SD400-1.01B ixus70_sd1000-101b ixus80_sd1100-100c ixus850_sd800-100e ixus860_SD870-1.00C S2IS (unknown sub) s3is-100a tx1-101b No failures or crashes have been reported. The pass/fail in the console only provides a general indication. The logfile should be examined to be sure everything is working. The log is formatted such that most lines end with OK or ERR followed by possible return values, and then PASS, FAIL, or NA. *'PASS' or FAIL indicate whether the test received the result it expected. *'OK' and ERR indicate whether the function being tested reported success or failure, which may represent a PASS or FAIL depending on circumstances. *'NA' means the script doesn't count the result of that particular call as a pass or fail, because the results are variable or not meaningful. More informations and a sample log file can be found in reyalp's forum post: preliminary lua iolib and oslib port * The script also serves as an example of how to use the libraries. * Since version 0.6.5 / changeset #541 the test script in its latest version is included in the distribution directory CHDK/SCRIPTS/TEST/LLIBTST.LUA in the 'complete' [http://mighty-hoernsche.de/chdk/ Autobuild download] package, it is also available from here. Some LUA scripts ---- yet another accurate intervalometer -- rem 20080805 @title yet another accurate intervalometer @param a Duration (min)/-1 disable @default a -1 @param b Duration (sec)/n of seqs @default b 5 @param c Delay 1st sequence (min) @default c 0 @param d Delay 1st sequence (sec) @default d 3 @param e Trigger every n min @default e 0 @param f ...every n sec @default f 3 @param g ...every .n sec @default g 0 @param h Endless? @default h 0 @param i Seq dur (m)/-1 @default i -1 @param j Seq dur (s)/n of shots/seq @default j 1 -- drivemode_continuous = 1 if a < -1 then a = -1 end if ( a > -1 and b < 0 ) then b = 0 end if ( a -1 and b < 1 ) then b = 1 end if c < 0 then c = 0 end if d < 0 then d = 0 end if e < 0 then e = 0 end if f < 0 then f = 0 end if g < 0 then g = 0 end if ( h < 0 or h > 1 ) then h = 0 end if i < -1 then i = -1 end if ( i > -1 and j < 0 ) then j = 0 end if ( i -1 and j < 1 ) then j = 1 end if (( i -1 and j > 1 ) or i > -1 ) then if get_drive_mode() ~= drivemode_continuous then print( "set drive mode" ) print( "to continuous" ) sleep(1500) cannot_continue = true end end if a > -1 then duration = a*60000 + b*1000 else duration = b end delay_first= c*60000 + d*1000 if g < 0 then delay = e*60000 + f*1000 + g*10 else delay = e*60000 + f*1000 + g*100 end if i > -1 then sequence = i*60000 + j*1000 else sequence = j end function shoot_by_numbers( sequence_target ) sequence_current = 0 repeat tick_target = get_tick_count() + delay sequence_current = sequence_current + 1 print( "sequence " .. sequence_current .. " of " .. sequence_target ) if i -1 then shoot_count( sequence ) else shoot_tick( sequence ) end while ( get_tick_count() < tick_target and sequence_current < sequence_target ) do end until sequence_current >= sequence_target end function shoot_by_duration( duration ) duration_target = get_tick_count() + duration repeat tick_target = get_tick_count() + delay if i -1 then shoot_count( sequence ) else shoot_tick( sequence ) end print( (duration_target-get_tick_count())/1000 .. " sec to go" ) while ( get_tick_count() < tick_target and get_tick_count() < duration_target ) do end until get_tick_count() >= duration_target end function shoot_forever() tick_initial = get_tick_count() sequence_current = 0 repeat tick_target = get_tick_count() + delay sequence_current = sequence_current + 1 print( "sequence " .. sequence_current ) if i -1 then shoot_count( sequence ) else shoot_tick( sequence ) end print ( (get_tick_count()-tick_initial)/1000 .. " sec elapsed") while ( get_tick_count() < tick_target ) do end until false end function shoot_count( count_inc ) count_target = get_exp_count() + count_inc if count_target > 9999 then count_target = count_target - 9999 end press( "shoot_half" ) press( "shoot_full" ) repeat until get_exp_count() count_target release( "shoot_full" ) repeat until get_shooting() false end function shoot_tick( tick_duration ) tick_target = get_tick_count() + tick_duration press( "shoot_half" ) press( "shoot_full" ) while ( get_tick_count() < tick_target ) do end release( "shoot_full" ) repeat until get_shooting() false end if not cannot_continue then tick_target = get_tick_count() + delay_first print( "waiting " .. delay_first/1000 .. " sec" ) while ( get_tick_count() < tick_target ) do tick_current = get_tick_count() end if h 1 then shoot_forever() end if a > -1 then shoot_by_duration( duration ) else shoot_by_numbers( duration ) end end ---- Char input demo This is a demo script in Lua. You can input a text as a folder name, if you want the folder will be created (A/name). In the script parameter menu you can control the keyboard delay for repeating. It was released by 'msl' in the CHDK forum in this post, a german version is available at www.wirklemms.de Using the script: :first consol menu <--> select a char in 1 step in/out select a char in 5 steps SET write a char +/- delete a char UP Selection uppercase, lowercase and special cases DOWN return to first char (a) MENU ready and go to the second Menu :second consol menu display the input SET return to new input DISP write the input name as folder and close the script MENU close the script -- rem 26-Oct-2008 by msl @title char input @param f key delay @default f 200 key_delay = f output="" function button() local x = 0 repeat wait_click(key_delay) if is_pressed "remote" then key = "REMOTE" x = 1 end if is_pressed "set" then key = "SET" x = 1 end if is_pressed "erase" then key = "ERASE" x = 1 end if is_pressed "display" then key = "DISP." x = 1 end if is_pressed "menu" then key = "MENU" x = 1 end if is_pressed "up" then key = "UP" x = 1 end if is_pressed "down" then key = "DOWN" x = 1 end if is_pressed "left" then key = "LEFT" x = 1 end if is_pressed "right" then key = "RIGHT" x = 1 end if is_pressed "zoom_in" then key = "ZOOM_IN" x = 1 end if is_pressed "zoom_out" then key = "ZOOM_OUT" x = 1 end until x 1 set_led (8,1) sleep (10) set_led (8,0) end function tabToStr() output=table.concat(word) end function input() abc_lower = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p", "q","r","s","t","u","v","w","x","y","z"} abc_upper = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P", "Q","R","S","T","U","V","W","X","Y","Z"} abc_spec = {"0","1","2","3","4","5","6","7","8","9",",",".","-",";",":","_", "/",".JPG",".CRW",".CR2",".THM",".WAV",".LUA",".BAS",".TXT"," "} word = {} a=0 b=0 actChar="" repeat print("<-->SET +/-MENU") print("in/out UPDOWN") button() cls() if key "RIGHT" then a=a+1 end if key "LEFT" then a=a-1 end if key "UP" then b=b+1 end if key "DOWN" then a=1 end if key "ZOOM_IN" then a=a+5 end if key "ZOOM_OUT" then a=a-5 end if a >= 27 then a=1 end if a <= 0 then a=26 end if b>=3 then b=0 end if b 0 then actChar=abc_lowera char_info="lower case" end if b 1 then actChar=abc_uppera char_info="upper case" end if b 2 then actChar=abc_speca char_info="special char" end print(char_info..": "..actChar) if key "SET" then table.insert(word,actChar) end if key "ERASE" then table.remove(word) end tabToStr() print("input: "..output) print (" ") until key "MENU" end function make_dir(dirname) os.mkdir(dirname) end x=0 repeat input() cls() print("INPUT: "..output) print("SET new input") print("DISP make dir and stop") print("MENU stop") button() if key "SET" then cls() end if key "DISP." then dir = "A/"..output make_dir(dir) print "ready" sleep(3000) x=1 end if key "MENU" then print "ready" sleep(3000) x=1 end until x 1 -- ***possible commands*** function make_dir(dirname) os.mkdir(dirname) end function delete_dir(dirname) os.remove(dirname) end function rename_file(old_filename, new_filename) os.rename(old_filename, new_filename) end function delete_file(filename) os.remove(filename) end ***missing commands*** -os.list_dir ---- Universal Tv mode This is a script for a enhanced tv mode. It should work with all cameras, even with models with or without tv mode. It was released by 'msl' in the CHDK forum in this post,a german version is available at www.wirklemms.de'' *You can select a Tv value from 64s to 1/10000s *The zoom can be used while running the script *You can select a focus mode *Continuous shooting is also possible Using the script: :The console menu: <--> select a Tv value UPDOWN zooming DISP. select a focus mode SETRemote shooting MENU exit In the script parameter menu you can adjust the start Tv value, the key delay und the shoot delay. --[[ rem 26-Oct-2008 by msl @title Tv mode universal @param a integer value Tv @default a 0 @param b key delay ms @default b 80 @param c shoot delay s @default c 0 ]] function print_Tv() tv_output = {"64s","50s","40s","32s","25s","20s","15s","13s","10s","8s","6s", "5s","4s","3.2s","2.5s","2s","1.6s","1.3s","1s","0.8s","0.6s","0.5s","0.4s", "0.3s","1/4s","1/5s","1/6s","1/8s","1/10s","1/13s","1/15s","1/20s","1/25s", "1/30s","1/40s","1/50s","1/60s","1/80s","1/100s","1/125s","1/160s","1/200s", "1/250s","1/320s","1/400s","1/500s","1/640s","1/800s","1/1000s","1/1250s", "1/1600s","1/2000s","1/2500s","1/3200s","1/4000s","1/5000s","1/6400s", "1/8000s","1/10000s"} print_tv = "Tv : "..tv_outputtv_input+19 end function mf_mode() mf_output = {"normal","macro","","infinite"} print_mf = "focus: "..mf_outputmf_input end function button() local x = 0 repeat wait_click(key_delay) if is_pressed "remote" then key = "SET" x = 1 end if is_pressed "set" then key = "SET" x = 1 end if is_pressed "menu" then key = "MENU" x = 1 end if is_pressed "display" then key = "DISP" x = 1 end if is_pressed "left" then key = "LEFT" x = 1 end if is_pressed "right" then key = "RIGHT" x = 1 end if is_pressed "up" then key = "UP" x = 1 end if is_pressed "down" then key = "DOWN" x = 1 end until x 1 set_led (8,1) sleep (10) set_led (8,0) end --script start if a < -18 then a = -18 end if a > 40 then a = 40 end if b < 50 then b = 50 end if c < 0 then c = 0 end tv_input = a key_delay = b shoot_delay = c max_zoom = get_zoom_steps() zoom = get_zoom() repeat if get_propset() 2 then mf_input = get_prop(6)+1 else mf_input = get_prop(11)+1 end cls() print_Tv() mf_mode() print("<--> ",print_tv) print("UpDown zoom:",zoom.."/".. max_zoom) print("DISP. ",print_mf) print("SET Remote shoot") print("MENU exit") button() if key "DISP" then mf_input = mf_input + 1 if mf_input 3 then mf_input = 4 end if mf_input 5 then mf_input = 1 end if get_propset() 2 then set_prop(6,mf_input-1) else set_prop(11,mf_input-1) end end if mf_input 2 then set_zoom(0) else if key "UP" then zoom = zoom + 1 if zoom > max_zoom then zoom = max_zoom end set_zoom(zoom) end if key "DOWN" then zoom = zoom - 1 if zoom < 0 then zoom = 0 end set_zoom(zoom) end end if key "RIGHT" then tv_input = tv_input + 1 end if key "LEFT" then tv_input = tv_input - 1 end if tv_input > 40 then tv_input = -18 end if tv_input < -18 then tv_input = 40 end if key "SET" then if shoot_delay > 0 then cls() print("wait "..shoot_delay.." s") sleep(shoot_delay * 1000) end TV = tv_input * 32 press "shoot_half" repeat until get_shooting() true if get_propset() 2 then set_prop(262,TV) else set_prop(69,TV) end press "shoot_full" release "shoot_full" release "shoot_half" end until key "MENU" ---- Listdir sample This scripts demonstrates the usage of os.listdir, scroll down the list with any key... -- @title Listdir rem 10-11-2008 by msl -- dir = os.listdir("A/CHDK", true) --'true' shows also . and .. count = table.getn(dir) x = 0 for i=1, count do print(i..".",diri) x = x + 1 if x 4 then wait_click(0) x = 0 end end print("------------") -- end of the list sleep(3000) wait_click(0) ---- Lotto demo This script is a little mathematical demo. --Example lua script that generates random numbers in various ways. --idea & code by PhyrePhoX and msl -- @title lotto demo @param n how many @default n 6 @param r max @default r 49 @param s min @default s 1 @param e exclusive 1=on @default e 1 @param o sort @default o 1 @param z sound 1=on @default z 1 --The comment block above will be read as parameters. --Declare a function for key request with led and sound feedback function button() -- declare function local x = 0 -- local variable only valid for this function repeat -- start loop wait_click(150) -- wait for key klick if is_pressed "set" then -- check which key ist pressed key = "SET" x = 1 end if is_pressed "menu" then key = "MENU" x = 1 end until x 1 -- end of loop set_led (8,1) -- led feedback sleep (10) set_led (8,0) if sound 1 then play_sound(4) end -- sound feedback if set end --Start script --Declare usefull variable names for the parameter variables count = n min = s max = r exclusive = e order = o sound = z --Check parameters, if wrong ending the script if (min >= max) then print("Wrong params, dumbass!;-)") wait_click(0) else if ((exclusive 1) and ((max - min + 1) ---- RAW development Since CHDK version 0.8.4 there are some new commands to merge/develop RAW images directly in the camera, read LUA Reference: RAW development for more informations. There's also a script to demonstrate the usage of this new feature: LUA Reference: Sample test script Category:Development Category:LUA